package com.bestcem.xm.user.service.impl;

import cn.hutool.core.util.URLUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bestcem.xm.common.core.constant.RegTypes;
import com.bestcem.xm.common.core.domain.web.ServiceResult;
import com.bestcem.xm.common.core.enums.AuthErrorCodeEnum;
import com.bestcem.xm.common.core.enums.ReqErrorCodeEnum;
import com.bestcem.xm.common.core.uitls.AESUtils;
import com.bestcem.xm.common.core.uitls.EmailUtil;
import com.bestcem.xm.common.core.uitls.SignUtil;
import com.bestcem.xm.user.dao.SsoDao;
import com.bestcem.xm.user.entity.pojo.SsoDO;
import com.bestcem.xm.user.entity.pojo.UserDO;
import com.bestcem.xm.user.enums.*;
import com.bestcem.xm.user.grpc.client.BaseOpenplatformGrpcService;
import com.bestcem.xm.user.grpc.client.dto.OpenPlatformApiCallCertDTO;
import com.bestcem.xm.user.mq.send.biz.UserMessageSendService;
import com.bestcem.xm.user.service.*;
import com.bestcem.xm.user.service.dto.group.UserGroupDTO;
import com.bestcem.xm.user.service.dto.group.UserUserGroupDTO;
import com.bestcem.xm.user.service.dto.user.*;
import com.bestcem.xm.user.service.third.GrowingIOService;
import com.bestcem.xm.user.util.business.DataBaseUtil;
import com.bestcem.xm.user.util.business.PwdUtil;
import com.bestcem.xm.user.util.business.UserJwtTokenUtil;
import com.bestcem.xm.user.util.business.UserUtil;
import com.bestcem.xm.user.util.convert.SsoConvert;
import com.bestcem.xm.user.util.convert.UserConvert;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.seimicrawler.xpath.JXDocument;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * SSO单点登录Service
 *
 * @author wangyunyun <yunyun.wang@idiaoyan.com>
 * @version v1.0
 * @date 2022/3/29 10:30
 */
@Slf4j
@Service
public class SsoServiceImpl implements SsoService {

    public static final Pattern AUTH_URL_PATTERN = Pattern.compile("(\\{([a-zA-Z]+)})");

    @Resource
    private SsoConvert ssoConvert;

    @Resource
    private UserConvert userConvert;

    @Resource
    private UserJwtTokenUtil userJwtTokenUtil;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private SsoDao ssoDao;

    @Autowired
    private UserService userService;

    @Autowired
    private UserBusinessService userBusinessService;

    @Autowired
    private GrowingIOService growingIOService;

    @Autowired
    private OrganizationService organizationService;

    @Autowired
    private UserUserRoleService userUserRoleService;

    @Autowired
    private RoleService roleService;

    @Autowired
    private UserUserAuthLogService userUserAuthLogService;

    //@Resource
    //private UserMessageSender userMessageSender;

    @Resource
    private UserMessageSendService userMessageSendService;

    @Autowired
    private BaseOpenplatformGrpcService baseOpenplatformGrpcService;

    @Autowired
    private UserUtil userUtil;

    @Resource
    private UserGroupService userGroupService;

    @Resource
    private UserUserGroupService userUserGroupService;

    @Override
    public ServiceResult<UserLoginDTO> silentLogin(String orgCode, String encrypt, String signature, String ipAddress) {
        // 校验参数
        if (StringUtils.isBlank(orgCode)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "The param :t_org_code lost");
        }
        if (StringUtils.isBlank(encrypt)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "The param :t_encrypt lost");
        }
        if (StringUtils.isBlank(signature)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "The param :t_signature lost");
        }
        // 通过orgCode查询并校验公司信息
        ServiceResult<OrganizationDTO> orgResult = this.checkAndGetOrgInfo(orgCode);
        if (!orgResult.isSuccess()) {
            ServiceResult<UserLoginDTO> orgFail = ServiceResult.fail(orgResult.getMsg());
            return orgFail.setErrorCode(orgResult.getErrorCode());
        }
        OrganizationDTO organizationDTO = orgResult.getData();

        // 解密加密参数encrypt并校验
        ServiceResult<SsoSilentLoginDecrptDTO> decodeResult = aesDecodeAndCheckEncrypt(organizationDTO.getOrgId(), encrypt, signature);
        if (!decodeResult.isSuccess()) {
            ServiceResult<UserLoginDTO> failRt = ServiceResult.fail(decodeResult.getMsg());
            failRt.setErrorCode(decodeResult.getErrorCode());
            return failRt;
        }

        SsoSilentLoginDecrptDTO decrptDTO = decodeResult.getData();
        // 查询用户
        UserDTO userDTO = this.queryUser(decrptDTO, organizationDTO.getOrgId());
        if (userDTO == null) {
            // 用户不存在，则创建静默登陆用户
            userDTO = createSilentUser(decrptDTO, organizationDTO.getOrgId());

            if (userDTO == null) {
                return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "创建静默登陆用户失败， 请重试");
            }
        }

        // 创建用户登录日志
        userDTO.setUpdateTime(DataBaseUtil.getDate());
        userBusinessService.updateUserLastLoginTime(userDTO, ipAddress);

        // 设置返回值
        return ServiceResult.success(buildSilentLoginResult(userDTO, organizationDTO, decrptDTO));
    }

    @Override
    public ServiceResult<UserLoginDTO> casLogin(String orgCode, String ticket, String service, String ipAddress) {
        // 校验参数
        if (StringUtils.isBlank(orgCode)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "The param :t_org_code lost");
        }
        // 通过orgCode查询并校验公司信息
        ServiceResult<OrganizationDTO> orgResult = this.checkAndGetOrgInfo(orgCode);
        if (!orgResult.isSuccess()) {
            ServiceResult<UserLoginDTO> orgFail = ServiceResult.fail(orgResult.getMsg());
            return orgFail.setErrorCode(orgResult.getErrorCode());
        }
        OrganizationDTO organizationDTO = orgResult.getData();

        // 通过orgId查询公司的sso信息
        SsoDTO ssoDTO = this.getUsableSsoByOrgId(organizationDTO.getOrgId()).getData();
        if (ssoDTO == null) {
            return ServiceResult.fail(AuthErrorCodeEnum.SSO_FORBIDDEN, "organization not support sso auth.");
        }

        // 请求验证ST,并获取用户信息: user_name name
        ServiceResult<UserDTO> userInfoResult = checkStBySso(ssoDTO, ticket, service);
        if (!userInfoResult.isSuccess()) {
            ServiceResult<UserLoginDTO> updateUserFail = ServiceResult.fail(userInfoResult.getMsg());
            return updateUserFail.setErrorCode(userInfoResult.getErrorCode());
        }
        UserDTO userInfo = userInfoResult.getData();

        // 通过用户名查询用户
        List<UserDTO> existUsers = this.queryUserByUserName(userInfo.getUserName(), organizationDTO.getOrgId());
        UserDTO userDTO = CollectionUtils.isNotEmpty(existUsers) ? existUsers.get(0) : null;

        if (userDTO == null) {
            // 用户不存在，则创建CAS登陆用户
            userDTO = this.createCasUser(userInfo, organizationDTO);

            if (userDTO == null) {
                return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "创建静默登陆用户失败， 请重试");
            }
        }

        // 创建用户登录日志
        userDTO.setUpdateTime(DataBaseUtil.getDate());
        userBusinessService.updateUserLastLoginTime(userDTO, ipAddress);

        // 设置返回值
        return ServiceResult.success(buildCasLoginResult(userDTO, organizationDTO));
    }

    /**
     * 通过orgCode查询并校验公司信息
     *
     * @param orgCode
     * @return ServiceResult<OrganizationDTO>
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/30 16:48
     */
    private ServiceResult<OrganizationDTO> checkAndGetOrgInfo(String orgCode) {
        // 通过orgCode查询并校验公司信息
        OrganizationDTO organizationDTO = organizationService.selectByOrgCode(orgCode).getData();
        if (organizationDTO == null || !OrganizationStatusEnum.ACTIVATED.getStatus().equals(organizationDTO.getStatus())) {
            return ServiceResult.fail(AuthErrorCodeEnum.ORG_NOT_EXISTS, "organization code not exist.");
        }
        // 校验公司套餐到期时间
        if (organizationDTO.getPackageExpireTime() != null) {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            String s = sdf.format(DataBaseUtil.getDate());
            Date now = null;
            try {
                now = sdf.parse(s);
            } catch (ParseException e) {
                log.error("日期转换错误", e);
                return ServiceResult.fail("日期转换错误");
            }
            if (now.after(organizationDTO.getPackageExpireTime())) {
                return ServiceResult.fail(AuthErrorCodeEnum.ORG_EXPIRE, UserJwtTokenUtil.formatDate(organizationDTO.getPackageExpireTime()));
            }
        }
        //返回
        return ServiceResult.success(organizationDTO);
    }

    /**
     * 请求验证ST,并获取用户信息
     *
     * @param ssoDTO
     * @param ticket
     * @param service
     * @return ServiceResult<UserDTO>
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/29 19:52
     */
    private ServiceResult<UserDTO> checkStBySso(SsoDTO ssoDTO, String ticket, String service) {
        // 校验参数
        if (StringUtils.isBlank(ssoDTO.getConfig())) {
            return ServiceResult.fail(AuthErrorCodeEnum.SSO_ERROR, "sso config 配置不全");
        }
        SsoConfigDTO ssoConfigDTO = JSON.parseObject(ssoDTO.getConfig(), SsoConfigDTO.class);
        if (!"kdxf".equals(ssoConfigDTO.getFunc())) {
            return ServiceResult.fail(AuthErrorCodeEnum.SSO_ERROR, "sso config func 配置匹配失败");
        }
        if (StringUtils.isBlank(ssoConfigDTO.getAuth()) || StringUtils.isBlank(ssoConfigDTO.getValidateCert())) {
            return ServiceResult.fail(AuthErrorCodeEnum.SSO_ERROR, "sso config 配置不全");
        }
        // 生成调用第三放单点登陆系统的认证链接
        Map<String, String> parameters = new HashMap<>();
        parameters.put("ticket", ticket);
        parameters.put("service", service);
        String authUrl = buildAuthUrl(ssoConfigDTO.getAuth(), parameters);
        // 调用第三放单点登陆系统获取用户信息
        ResponseEntity<String> response = null;
        try {
            Map<String, String> uriVariables = new HashMap<>();
            uriVariables.put("validate_cert", ssoConfigDTO.getValidateCert());
            response = restTemplate.getForEntity(authUrl, String.class, uriVariables);
        } catch (Exception e) {
            log.error("调用第三放单点登陆系统获取用户信息服务失败: authUrl=[{}], ssoDTO=[{}]", authUrl, JSON.toJSONString(ssoDTO));
            log.error("调用第三放单点登陆系统获取用户信息服务失败", e);
        }

        if (response != null && HttpStatus.OK.equals(response.getStatusCode())) {
            String responseBody = URLUtil.decode(response.getBody(), StandardCharsets.UTF_8);
            Document doc = Jsoup.parse(responseBody);
            JXDocument jxDocument = JXDocument.create(doc);
            Object authenticationSuccess = jxDocument.selOne("//authenticationsuccess");
            if (!"true".equals(JSON.toJSONString(authenticationSuccess))) {
                log.error("调用第三放单点登陆系统获取用户信息服务失败: responseBody = [{}]", responseBody);
                return ServiceResult.fail(AuthErrorCodeEnum.SSO_ERROR, "调用第三放单点登陆系统获取用户信息服务失败");
            }
            Object userName = jxDocument.selOne("//user");
            Object name = jxDocument.selOne("//username");
            // 设置返回值
            UserDTO userInfo = new UserDTO();
            userInfo.setUserName(JSON.toJSONString(userName));
            userInfo.setName(JSON.toJSONString(name));
            return ServiceResult.success(userInfo);
        }

        return ServiceResult.fail(AuthErrorCodeEnum.SSO_ERROR, "调用第三放单点登陆系统获取用户信息服务失败");
    }

    /**
     * 生成调用第三放单点登陆系统的认证链接
     *
     * @param tempalte
     * @param parameters
     * @return String
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/29 19:55
     */
    private String buildAuthUrl(String tempalte, Map<String, String> parameters) {
        Matcher m = AUTH_URL_PATTERN.matcher(tempalte);
        StringBuffer stringBuffer = new StringBuffer();
        while (m.find()) {
            String key = m.group(2);
            String value = null;
            if (parameters.containsKey(key)) {
                value = parameters.get(key);
            }
            value = (value == null) ? "" : value;
            m.appendReplacement(stringBuffer, value);
        }
        m.appendTail(stringBuffer);
        return stringBuffer.toString();
    }

    /**
     * 设置用户登录返回值-sso cas 登陆
     *
     * @param userDTO
     * @param organizationDTO
     * @return UserLoginDTO
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/29 17:25
     */
    private UserLoginDTO buildCasLoginResult(UserDTO userDTO, OrganizationDTO organizationDTO) {
        // 设置认证信息
        AuthDTO authDTO = buildAuthDTO(userDTO, organizationDTO);

        // 生成token和refreshToken
        String token = userJwtTokenUtil.generateToken(authDTO);
        String refreshToken = userJwtTokenUtil.generateRefreshToken(authDTO.getOrgCode(), authDTO.getUserId());

        // 设置返回值
        UserLoginDTO userLoginDTO = new UserLoginDTO();
        userLoginDTO.setToken(token);
        userLoginDTO.setRefreshToken(refreshToken);
        return userLoginDTO;
    }

    /**
     * 设置用户登录返回值-静默登陆
     *
     * @param userDTO
     * @param organizationDTO
     * @param decrptDTO
     * @return
     */
    private UserLoginDTO buildSilentLoginResult(UserDTO userDTO, OrganizationDTO organizationDTO, SsoSilentLoginDecrptDTO decrptDTO) {
        String packageName = null;
        // 设置认证信息
        AuthDTO authDTO = buildAuthDTO(userDTO, organizationDTO);

        // 生成token和refreshToken
        String token = userJwtTokenUtil.generateToken(authDTO);
        String refreshToken = userJwtTokenUtil.generateRefreshToken(authDTO.getOrgCode(), authDTO.getUserId());

        // 设置growingio信息
        GrowingioDTO growingioDTO = growingIOService.getUserProperty(userDTO, organizationDTO, packageName);

        // 设置返回值
        UserLoginDTO userLoginDTO = new UserLoginDTO();
        userLoginDTO.setOrgCode(organizationDTO.getCode());
        userLoginDTO.setGrowingio(growingioDTO);
        userLoginDTO.setToken(token);
        userLoginDTO.setRefreshToken(refreshToken);
        userLoginDTO.setTarget(decrptDTO.getTarget());
        userLoginDTO.setClearSession(decrptDTO.getClearSession());
        return userLoginDTO;
    }

    /**
     * 设置认证信息
     *
     * @param userDTO
     * @param organizationDTO
     * @return AuthDTO
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/29 17:23
     */
    @Override
    public AuthDTO buildAuthDTO(UserDTO userDTO, OrganizationDTO organizationDTO) {
        AuthDTO authDTO = AuthDTO.builder()
                .packageExpireTime(UserJwtTokenUtil.formatDate(organizationDTO.getPackageExpireTime()))
                .orgCode(organizationDTO.getCode())
                .orgId(organizationDTO.getOrgId())
                .userId(userDTO.getUserId())
                .realName(userDTO.getName())
                .avatar(userDTO.getAvatar())
                .build();

        // 设置用户是否是系统管理员和高级管理员
        authDTO.setSuperRole(userBusinessService.checkSuper(userDTO.getUserId(), userDTO.getOrgId()).getData());
        authDTO.setSenior(userBusinessService.checkSenior(userDTO.getUserId(), userDTO.getOrgId()).getData());

        return authDTO;
    }

    /**
     * 建静默登陆用户
     *
     * @param decrptDTO sso加密用户对象
     * @param orgId     公司id
     * @return UserDTO
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/29 15:11
     */
    private UserDTO createSilentUser(SsoSilentLoginDecrptDTO decrptDTO, String orgId) {
        // 设置参数
        UserDTO userDTO = new UserDTO();
        userDTO.setOrgId(orgId);
        userDTO.setUserName(decrptDTO.getUsername());
        userDTO.setName(decrptDTO.getName());
        userDTO.setPhone(decrptDTO.getPhone());
        userDTO.setStatus(UserStatusEnum.INACTIVATED.getStatus());
        userDTO.setSalt(PwdUtil.genSalt());
        userDTO.setPassword(PwdUtil.getEncodePwd(PwdUtil.genPassword(), userDTO.getSalt()));
        // 新增用户
        ServiceResult<String> result = this.createUser(userDTO, null, null, null, false);
        if (!result.isSuccess()) {
            return null;
        }
        return userDTO;
    }

    /**
     * 创建CAS登陆用户
     *
     * @param userInfo
     * @param organizationDTO
     * @return UserDTO
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/29 17:10
     */
    private UserDTO createCasUser(UserDTO userInfo, OrganizationDTO organizationDTO) {
        // 设置参数
        UserDTO userDTO = new UserDTO();
        userDTO.setOrgId(organizationDTO.getOrgId());
        userDTO.setUserName(userInfo.getUserName());
        userDTO.setName(userInfo.getName());
        userDTO.setEmail(String.format("%s@%s.com", userInfo.getUserName(), organizationDTO.getCode()));
        userDTO.setStatus(UserStatusEnum.INACTIVATED.getStatus());
        userDTO.setSalt(PwdUtil.genSalt());
        userDTO.setPassword(PwdUtil.getEncodePwd(PwdUtil.genPassword(), userDTO.getSalt()));
        userDTO.setLastLoginTime(userInfo.getUpdateTime());
        userDTO.setCreateTime(userInfo.getUpdateTime());
        // 新增用户
        ServiceResult<String> result = userService.insert(userDTO);
        if (!result.isSuccess()) {
            return null;
        }
        return userDTO;
    }

    /**
     * 查询用户
     *
     * @param decrptDTO
     * @param orgId
     * @return UserDTO
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/29 15:08
     */
    private UserDTO queryUser(SsoSilentLoginDecrptDTO decrptDTO, String orgId) {
        List<UserDTO> existUsers = null;
        if (decrptDTO.getMatchSilentIdFlag() != null && decrptDTO.getMatchSilentIdFlag()) {
            UserDTO userQuery = new UserDTO();
            userQuery.setOrgId(orgId);
            userQuery.setSilentId(decrptDTO.getUsername());
            userQuery.setStatus(UserStatusEnum.ACTIVATED.getStatus());
            existUsers = userService.listByCondition(userQuery).getData();

        } else {
            existUsers = this.getUserByPhoneOrEmail(decrptDTO.getUsername(), orgId);
        }

        return CollectionUtils.isNotEmpty(existUsers) ? existUsers.get(0) : null;
    }

    /**
     * 通过用户名查询用户
     *
     * @param userName
     * @param orgId
     * @return List<UserDTO>
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/29 18:45
     */
    private List<UserDTO> queryUserByUserName(String userName, String orgId) {
        UserDTO userQuery = new UserDTO();
        userQuery.setUserName(userName);
        userQuery.setStatus(UserStatusEnum.ACTIVATED.getStatus());
        userQuery.setOrgId(orgId);
        return userService.listByCondition(userQuery).getData();
    }

    /**
     * 通过手机号或者邮箱或者用户名查询用户
     *
     * @param username 邮箱/手机号/用户名
     * @param orgId    公司id
     * @return
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/29 15:30
     */
    private List<UserDTO> getUserByPhoneOrEmail(String username, String orgId) {
        // 通过手机号查询用户
        List<UserDTO> existUsersByPhone = new ArrayList<>();
        if (Pattern.matches(RegTypes.MOBILE, username)) {
            UserDTO userQuueryByPhone = new UserDTO();
            userQuueryByPhone.setPhone(username);
            userQuueryByPhone.setStatus(UserStatusEnum.ACTIVATED.getStatus());
            userQuueryByPhone.setOrgId(orgId);
            existUsersByPhone = userService.listByCondition(userQuueryByPhone).getData();
        }
        // 通过邮箱查询用户
        List<UserDTO> existUsersByEmail = null;
        if (Boolean.TRUE.equals(EmailUtil.validateEmail(username))) {
            UserDTO userQueryByEmail = new UserDTO();
            userQueryByEmail.setEmail(username);
            userQueryByEmail.setStatus(UserStatusEnum.ACTIVATED.getStatus());
            userQueryByEmail.setOrgId(orgId);
            existUsersByEmail = userService.listByCondition(userQueryByEmail).getData();
        }

        // 通过用户名查询用户
        List<UserDTO> existUsersByUsername = this.queryUserByUserName(username, orgId);

        // 设置返回值
        if (CollectionUtils.isNotEmpty(existUsersByEmail)) {
            existUsersByPhone.addAll(existUsersByEmail);
        }
        if (CollectionUtils.isNotEmpty(existUsersByUsername)) {
            existUsersByPhone.addAll(existUsersByUsername);
        }
        // 用户的结果集去重
        return existUsersByPhone.stream().distinct().collect(Collectors.toList());
    }

    /**
     * 解密加密参数encrypt并校验
     *
     * @param orgId     公司id
     * @param encrypt
     * @param signature
     * @return ServiceResult<UserLoginDTO>
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/29 15:00
     */
    private ServiceResult<SsoSilentLoginDecrptDTO> aesDecodeAndCheckEncrypt(String orgId, String encrypt, String signature) {
        // 通过公司id获取对应的ApiCallCert
        OpenPlatformApiCallCertDTO apiCallCertDTO = baseOpenplatformGrpcService.getApiCallCertByOrgId(orgId);
        // 解密参数
        String arguments = AESUtils.decrypt(encrypt, apiCallCertDTO.getAesKey());
        if (arguments == null) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_DECRYPT_ERROR, "Params decode failed");
        }
        if (StringUtils.isBlank(arguments)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "The param :t_ts lost");
        }

        // 转换参数,并校验
        SsoSilentLoginDecrptDTO decrptDTO = JSON.parseObject(arguments, SsoSilentLoginDecrptDTO.class);
        if (StringUtils.isBlank(decrptDTO.getTs())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "The param :t_ts lost");
        }
        if (!NumberUtils.isDigits(decrptDTO.getTs())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_RANGE, "The param :t_ts not allowed");
        }
        // 60秒过期 比较单位毫秒
        if (System.currentTimeMillis() - (Long.parseLong(decrptDTO.getTs()) * 1000L) > 60000) {
            return ServiceResult.fail(ReqErrorCodeEnum.INVALID_TS, "The ts Invalid");
        }
        // 设置签名并校验
        String sign = this.genSignature(arguments, apiCallCertDTO.getSignatureKey());
        if (StringUtils.isBlank(sign) || !sign.equals(signature)) {
            return ServiceResult.fail(AuthErrorCodeEnum.INVALID_SIGN, "The signature Invalid");
        }

        if (StringUtils.isBlank(decrptDTO.getUsername())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "The param :t_user lost");
        }
        if (StringUtils.isNotBlank(decrptDTO.getSuperRole()) && !NumberUtils.isDigits(decrptDTO.getSuperRole())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_TYPE_ERROR, "The :t_super type error");
        }

        return ServiceResult.success(decrptDTO);
    }

    /**
     * 解析签名
     *
     * @param signature
     * @param signatureKey
     * @return String
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/29 1:42
     */
    private String genSignature(String signature, String signatureKey) {
        return SignUtil.genSign(JSON.parseObject(signature, HashMap.class), signatureKey);
    }

    @Override
    public ServiceResult<SsoDTO> selectBySelective(SsoDTO dto) {
        if (Objects.isNull(dto.getOrgId())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "orgId不能为空");
        }
        List<SsoDO> ssoDOS = ssoDao.selectBySelective(ssoConvert.dto2Do(dto));
        SsoDO ssoDO = CollectionUtils.isNotEmpty(ssoDOS) ? ssoDOS.get(0) : null;
        return ServiceResult.success(ssoConvert.do2Dto(ssoDO));
    }

    @Override
    public ServiceResult<HashMap<String, Object>> checkSso(String orgCode) {
        if (StringUtils.isNotBlank(orgCode)) {
            ServiceResult<OrganizationDTO> result = organizationService.selectByCodeAndStatus(orgCode, OrganizationStatusEnum.ACTIVATED.getStatus());

            if (result.isSuccess() && Objects.nonNull(result.getData())) {
                SsoDTO dto = new SsoDTO();
                dto.setOrgId(result.getData().getOrgId());
                dto.setStatus(UserSsoStatusEnum.ACTIVATED.getStatus());
                ServiceResult<SsoDTO> ssoResult = this.selectBySelective(dto);

                HashMap<String, Object> ssoInfo = new HashMap<>();
                if (ssoResult.isSuccess() && Objects.nonNull(ssoResult.getData())) {
                    ssoInfo.put("sso", true);

                    SsoDTO ssoDTO = ssoResult.getData();
                    JSONObject config = JSON.parseObject(ssoDTO.getConfig());
                    if (config.containsKey("return")) {
                        JSONArray array = JSON.parseArray(config.get("return").toString());
                        for (Object key : array) {
                            Object val = config.get(key);
                            ssoInfo.put(key.toString(), val);
                        }
                        return ServiceResult.success(ssoInfo);
                    }
                    return ServiceResult.success(ssoInfo);
                }
                ssoInfo.put("sso", false);
                return ServiceResult.success(ssoInfo);
            }
        }
        return ServiceResult.fail(AuthErrorCodeEnum.ORG_NOT_EXISTS, "orgCode不存在");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ServiceResult<Object> getToken(String orgCode, String userName, String name) {
        AuthDTO jwtData = new AuthDTO();

        OrganizationDTO organization = null;
        if (StringUtils.isNotBlank(orgCode)) {
            OrganizationDTO organizationDTO = new OrganizationDTO();
            organizationDTO.setCode(orgCode);
            organizationDTO.setStatus(OrganizationStatusEnum.ACTIVATED.getStatus());
            ServiceResult<List<OrganizationDTO>> organizationResult = organizationService.selectBySelective(organizationDTO);

            List<OrganizationDTO> organizationDTOList = organizationResult.getData();
            if (!organizationResult.isSuccess() || CollectionUtils.isEmpty(organizationDTOList) || Objects.isNull(organizationDTOList.get(0))) {
                return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "orgCode不正确");
            }

            organization = organizationDTOList.get(0);
            if (Objects.nonNull(organization.getPackageExpireTime())) {
                Date packageExpireTime = organization.getPackageExpireTime();
                Date now = DataBaseUtil.getDate();
                if (now.getTime() > packageExpireTime.getTime()) {
                    return ServiceResult.fail(AuthErrorCodeEnum.ORG_EXPIRE, packageExpireTime.toString());
                }
                jwtData.setPackageExpireTime(packageExpireTime.toString());
            }
            jwtData.setOrgCode(organization.getCode());
            jwtData.setOrgId(organization.getOrgId());
        }

        if (Objects.isNull(organization)) {
            return ServiceResult.fail(AuthErrorCodeEnum.ORG_NOT_EXISTS);
        }

        SsoDO ssoDO = new SsoDO();
        ssoDO.setOrgId(organization.getOrgId());
        List<SsoDO> ssoDOS = ssoDao.selectBySelective(ssoDO);
        if (CollectionUtils.isEmpty(ssoDOS)) {
            return ServiceResult.fail(AuthErrorCodeEnum.SSO_FORBIDDEN, "sso not support sso auth.");
        }
        if (StringUtils.isBlank(ssoDOS.get(0).getConfig())) {
            return ServiceResult.fail(AuthErrorCodeEnum.SSO_FORBIDDEN, "organization not support sso auth.");
        }

        UserDTO userDTO = new UserDTO();
        userDTO.setOrgId(jwtData.getOrgId());
        userDTO.setUserName(userName);
        log.info("sso login userName:{}", userName);
        ServiceResult<List<UserDO>> userServiceResult = userService.selectByOrgIdSelective(userDTO);
        if (userServiceResult.isSuccess() && CollectionUtils.isNotEmpty(userServiceResult.getData()) && Objects.nonNull(userServiceResult.getData().get(0))) {
            UserDO userDO = userServiceResult.getData().get(0);
            if (!userDO.getStatus().equals(UserStatusEnum.ACTIVATED.getStatus())) {
                return ServiceResult.fail(AuthErrorCodeEnum.USER_NOT_EXISTS.getMsg(), "账号未激活");
            }
        } else {
            String salt = PwdUtil.genSalt();
            String password = PwdUtil.genPassword();
            String pwd = PwdUtil.getEncodePwd(password, salt);

            String email = (userName + "@" + orgCode + ".com").toLowerCase();
            UserDTO userCreateDTO = new UserDTO();
            userCreateDTO.setOrgId(jwtData.getOrgId())
                    .setUserName(userName)
                    .setName(name)
                    .setEmail(email)
                    .setPassword(pwd)
                    .setSalt(salt)
                    .setStatus(UserStatusEnum.ACTIVATED.getStatus());
            ServiceResult<String> createResult = createUser(userCreateDTO, new ArrayList<>(), null, new ArrayList<>(), true);
            if (!createResult.isSuccess()) {
                return ServiceResult.fail(createResult.getData(), createResult.getMsg());
            }

            // 发送mq消息
            userMessageSendService.publishUserCreate(createResult.getData(), new ArrayList<>());

            UserDTO user = new UserDTO();
            user.setOrgId(jwtData.getOrgId());
            user.setUserName(userName);
            user.setStatus(UserStatusEnum.ACTIVATED.getStatus());
            userServiceResult = userService.selectByOrgIdSelective(user);
        }

        List<UserDO> resultData = userServiceResult.getData();
        if (resultData.isEmpty()) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "用户不存在");
        }
        UserDO userDO = resultData.get(0);
        jwtData.setUserId(userDO.getUserId());
        jwtData.setRealName(userDO.getUserName());
        jwtData.setAvatar(userDO.getAvatar());

        ServiceResult<String> adminRole = roleService.getAdminRole(jwtData.getOrgId());
        ServiceResult<List<String>> roleIdsByUserId = userUserRoleService.listRoleIdsByUserId(userDO.getUserId());
        if (roleIdsByUserId.isSuccess() && adminRole.isSuccess() && Objects.nonNull(adminRole.getData()) && roleIdsByUserId.getData().contains(adminRole.getData())) {
            jwtData.setSuperRole(UserEnum.USER_YES.getFlag());
        }

        // 生成jwt
        AuthDTO authDTO = buildAuthDTO(userConvert.do2Dto(userDO), organization);
        String token = userJwtTokenUtil.generateToken(authDTO);
        String refreshToken = userJwtTokenUtil.generateRefreshToken(orgCode, userDO.getUserId());

        // 更新最近登录时间
        updateLastLoginTime(userDO.getUserId(), userDO.getOrgId());

        TokenDTO4Sso tokenDTO4Sso = new TokenDTO4Sso();
        tokenDTO4Sso.setToken(token);
        tokenDTO4Sso.setRefreshToken(refreshToken);
        return ServiceResult.success(tokenDTO4Sso);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ServiceResult<String> createUser(UserDTO dto, List<String> roleIdList, String rootGroupId, List<String> groupIdList, Boolean checkParams) {
        userService.checkUserQuota(dto.getOrgId(), 1);

        dto.setAcceptFlag(UserEnum.USER_NO.getFlag());
        boolean checkUserIsInner = userUtil.checkUserIsInner(dto.getPhone(), dto.getEmail(), "");
        dto.setInnerFlag(BooleanStatusEnum.toInteger(checkUserIsInner));

        ServiceResult<String> result = ssoCreateUser(dto, rootGroupId, groupIdList, checkParams);
        if (!result.isSuccess()) {
            return result;
        }
        if (CollectionUtils.isNotEmpty(roleIdList)) {
            userUserRoleService.insertUserRoleList(dto.getOrgId(), dto.getUserId(), roleIdList, null);
        }
        userService.updateUserConsumption(dto.getOrgId(), 1);
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ServiceResult<String> ssoCreateUser(UserDTO dto, String rootGroupId, List<String> groupIdList, Boolean checkParams) {
        String userName = dto.getUserName();
        if (Boolean.TRUE.equals(checkParams)) {
            if (StringUtils.isBlank(userName)) {
                return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "userName不能为空");
            }
            if (userName.contains("@")) {
                return ServiceResult.fail(ReqErrorCodeEnum.PARAM_FORMAT, "userName不能包含\"@\"符号");
            }
            String email = dto.getEmail();
            if (StringUtils.isBlank(email)) {
                return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "email不能为空");
            }
            dto.setEmail(email.toLowerCase());
            dto.setAcceptFlag(BooleanStatusEnum.NO.getStatus());
        }
        ServiceResult<String> userIdService = userService.insert(dto);
        String userId = userIdService.getData();

        if (CollectionUtils.isNotEmpty(groupIdList)) {
            if (StringUtils.isBlank(rootGroupId)) {
                ServiceResult<UserGroupDTO> byId = userGroupService.getById(groupIdList.get(0));
                if (byId.isSuccess()) {
                    List<String> groupParentIds = JSON.parseArray(byId.getData().getGroupParentIds(), String.class);
                    rootGroupId = groupParentIds.get(0);
                }
            }
            ArrayList<UserUserGroupDTO> userUserGroups = new ArrayList<>();
            for (String groupId : groupIdList) {
                UserUserGroupDTO userUserGroupDTO = new UserUserGroupDTO();
                userUserGroupDTO.setUserGroupId(DataBaseUtil.generateId());
                userUserGroupDTO.setUserId(userId);
                userUserGroupDTO.setRootGroupId(rootGroupId);
                userUserGroupDTO.setGroupId(groupId);
                userUserGroups.add(userUserGroupDTO);
            }
            userUserGroupService.insertUserGroupList(userUserGroups);
        }

        userMessageSendService.publishUserCreate(userId, groupIdList);
        return ServiceResult.success(userId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ServiceResult<String> updateLastLoginTime(String userId, String orgId) {
        UserDTO dto = new UserDTO();
        dto.setUserId(userId)
                .setLastLoginTime(DataBaseUtil.getDate());
        userService.updateById(dto);

        UserAuthLogDTO userAuthLogDTO = new UserAuthLogDTO();
        userAuthLogDTO.setUserId(userId);
        userAuthLogDTO.setOrgId(orgId);
        userAuthLogDTO.setOpType(UserAuthLogOpTypeEnum.LOGIN.getType());
        userAuthLogDTO.setCreatorId(userId);
        userAuthLogDTO.setOperatorId(userId);
        return userUserAuthLogService.insert(userAuthLogDTO);
    }

    @Override
    public ServiceResult<SsoDTO> create(SsoDTO ssoDTO) {
        // 校验参数
        if (ssoDTO == null || StringUtils.isBlank(ssoDTO.getOrgId()) || StringUtils.isBlank(ssoDTO.getConfig())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, ReqErrorCodeEnum.PARAM_LOST.getMsg());
        }
        if (ssoDTO.getStatus() != null && UserSsoStatusEnum.getByStatus(ssoDTO.getStatus()) == null) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_RANGE, String.format("参数错误. status = %s", ssoDTO.getStatus()));
        }

        // 设置参数
        Date now = DataBaseUtil.getDate();
        ssoDTO.setStatus(ssoDTO.getStatus() == null ? UserSsoStatusEnum.ACTIVATED.getStatus() : ssoDTO.getStatus());
        ssoDTO.setCreateTime(now);
        ssoDTO.setUpdateTime(now);

        // 创建sso
        ServiceResult<String> result = this.insertSelective(ssoDTO);

        // 设置返回值
        ssoDTO.setSsoId(result.getData());
        return ServiceResult.success(ssoDTO);
    }

    @Override
    public ServiceResult<String> insertSelective(SsoDTO ssoDTO) {
        // 新增并返回
        String ssoId = ssoDao.insertSelective(ssoConvert.dto2Do(ssoDTO));
        ssoDTO.setSsoId(ssoId);
        return ServiceResult.success(ssoId);
    }

    @Override
    public ServiceResult<SsoDTO> getUsableSsoByOrgId(String orgId) {
        if (StringUtils.isBlank(orgId)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, ReqErrorCodeEnum.PARAM_LOST.getMsg());
        }
        SsoDO ssoDO = new SsoDO();
        ssoDO.setOrgId(orgId);
        ssoDO.setStatus(UserSsoStatusEnum.ACTIVATED.getStatus());
        List<SsoDO> ssoDOS = ssoDao.selectBySelective(ssoDO);
        if (CollectionUtils.isNotEmpty(ssoDOS)) {
            return ServiceResult.success(ssoConvert.do2Dto(ssoDOS.get(0)));
        }
        return ServiceResult.success();
    }

}
